/**
 * $Id$
 *
 * Developed 2004 Sun Microsystems, Inc.
 *
 * Use is subject to licence terms.
 *
 * This software uses software developed by the Apache Software Foundation
 * Copyright (c) 1999-2004 The Apache Software Foundation.
 *
 */

package com.google.cse.emea.jct.utils.protocols.cpath;



import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
/**
 * Protocol Handler for finding Resources in the classpath. Implements the custom protocol cpath:
 * To use this Protocol Handler inside an Application you can register the protocol Handler using 
 * the command Line arg: -Djava.protocol.handler.pkgs=de.sun.commons.io.protocols.
 * 
 * a cpath url has the form:<p>
 * cpath:&lt;path&gt;
 * <p>A host part is not supported and will result in a MalformedURLException. The <em>path</em> will be
 * passed to a <em>ClassLoader.getResource()</em> call for final resolution. For backwards compatibility, 
 * we allow  one leading '/' in the path, which would normally cause the resource to not be found by 
 * <em>getResource()</em>. Therefore, URLs of the following forms will all yield the same results:
 * <ul>
 * <li>cpath:///this/is/the/path - no host and leading '/'</li>
 * <li>cpath:this/is/the/path - no host and no leading '/'</li>
 * </ul>
 * whereas this will not work:
 * <ul>
 * <li>cpath://this/is/the/path - host name 'this', MalformedURLException</li>
 * </ul>
 * 
 * @author bloemsma, jentra, sellc
 * @version $Revision$
 * @see http://java.sun.com/developer/onlineTraining/protocolhandlers
 */
public class Handler extends URLStreamHandler {
    
    private static String PROTOCOL ="cpath";
    private static String PROTOCOLPLUS =PROTOCOL+":";
    private static String PROTOCOL_SLASHES =PROTOCOLPLUS+"//";
    
    
  
    public static String getProtocolPrefix() {
        return PROTOCOL;
    }
        
    /**
     * Get the path to the Resource
     *
     * @param filename a resource locator, potentially prefixed by 'cpath:'
     * @return
     * @throws FileNotFoundException
     */
    public static InputStream getStream(String filename) throws FileNotFoundException {
        if (filename.startsWith(PROTOCOL_SLASHES)){
            InputStream stream =  Handler.class.getResourceAsStream(
                        filename.substring(PROTOCOL_SLASHES.length()));
            if (stream == null){
                throw new FileNotFoundException("Not found "+filename+" in classpath.");
            }
            return stream;
        }
        return new FileInputStream(filename);
    }
    /**
     * Get the path to the Resource
     *
     * @param filename a resource locator, potentially prefixed by 'cpath:'
     * @return
     * @throws FileNotFoundException
     */
    public static String locateFile(String filename) throws FileNotFoundException {
        if (filename.startsWith(PROTOCOLPLUS))
            filename=filename.substring(PROTOCOLPLUS.length());
        
        return locateFileResource(filename).getPath();
    }
    
    /** 
     * Get the File form cpath url.
     * This method returns a File object from an URL string, which will always fail for 
     * non-file-based resources
     */
    public static java.io.File getFile(String cpath) throws IOException {
        
        String file = locateFile(cpath);
        return new java.io.File(file);
    }
    
    /**
     * Get the url to find the resource
     *
     * @param filename
     * @return
     */
    private static URL locateFileResource(String filename) throws java.io.FileNotFoundException {
        // remove leading slashes

        if((filename == null) || filename.equals("")) {
            throw new IllegalArgumentException("filename must not be null or empty");
        }
        
        while (filename.startsWith("/"))
            filename=filename.substring(1);
        
        java.net.URL url = getClassLoader().getResource(filename);
        
        if(url != null) {
            return url;
        }

        //error analysis. Who needs this?
        java.util.StringTokenizer pathTokens = new java.util.StringTokenizer(filename,"/");
        StringBuffer path = new StringBuffer();
        
        while (pathTokens.hasMoreTokens()) {
            String token = pathTokens.nextToken();
            path.append(token);
            url = getClassLoader().getResource(path.toString());
            
            if (url == null) {
                break;
            }
            path.append("/");
        }
        throw new FileNotFoundException("Resource not found: " + filename+". Error in "+path);
    }
    
    /**
     * This method decides which classloader will be used by all
     * resource/classloading in this class. At the very least, you should use the current thread's
     * context loader. A better strategy is to use techniques shown in
     * http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html.
     */
    private static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }
    
    /**
     * Method openFileSource
     *
     * @param filename
     * @return
     * @throws FileNotFoundException
     * @throws IOException
    public static InputStream openFileSource(String filename) throws FileNotFoundException, IOException {
        
        if((filename == null) || filename.equals("")) {
            
            //throw new IllegalArgumentException("nothing to locate");
            return null;
        }
        
        File f   = new File(filename);
        URL  url = null;
        
        if(f.isFile() && f.canRead()) {
            url = f.toURL();
        } else {
            url = getClassLoader().getResource(filename);
        }
        
        if(url != null) {
            return url.openConnection().getInputStream();
        } else {
            throw new FileNotFoundException("File not found : " + filename);
        }
    }
     */
    
    /**
     * Method openFileTarget
     *
     * @param filename
     * @return
     * @throws FileNotFoundException
     * @throws IOException
    public static OutputStream openFileTarget(String filename)
    throws FileNotFoundException, IOException {
        
        if((filename == null) || filename.equals("")) {
            
            //throw new IllegalArgumentException("nothing to locate");
            return null;
        }
        
        URL url = getClassLoader().getResource(filename);
        
        if(url != null) {
            return url.openConnection().getOutputStream();
        } else {
            throw new FileNotFoundException("filename: " + filename);
        }
    }
     */
    
    /**
     * Class CPathConnection
    public static class CPathConnection extends URLConnection {
        
        URL delegateUrl;
        URLConnection delegateConnection;
        
        CPathConnection(URL url) throws MalformedURLException, IOException {
            super(url);
            delegateUrl = makeDelegateUrl(url);
            delegateConnection = delegateUrl.openConnection();
        }
        
        private URL makeDelegateUrl(URL url) throws IOException {
            
            String file = url.getHost() + url.getPath();
            URL res  = locateFileResource(file);
            
            return res;
        }
        
        public void connect() throws IOException {
            delegateConnection.connect();
        }
        
        public Object getContent() throws IOException {
            return delegateConnection.getContent();
        }
        
        public String getContentType() {
            return delegateConnection.getContentType();
        }
        
        public InputStream getInputStream() throws IOException {
            return delegateConnection.getInputStream();
        }
        
        public String getHeaderField(String string) {
            return delegateConnection.getHeaderField(string);
        }
        
        public Permission getPermission() throws IOException {
            return delegateConnection.getPermission();
        }
    }
 
    
    /**
     * Class CPathHandler
    public static class CPathHandler extends URLStreamHandler {
        
        protected URLConnection openConnection(URL u) throws IOException {
            return new CPathConnection(u);
        }
    }
    protected URLConnection openConnection(URL u) throws IOException {
        return new CPathConnection(u);
    }
     */
    
    protected URLConnection openConnection(URL url) throws java.io.IOException {
        
        if(url.getHost() != null && url.getHost().length() > 0) {
            throw new MalformedURLException("host not supported by this protocol");
        }
        String path = url.getPath();
        if(path.startsWith("/")) {
            path = path.substring(1);
        }
        URL actualURL = getClassLoader().getResource(path);
        
        if(actualURL == null) {
            // Fallback for Ant enviroment. 
            // Anyone who removes the follwowing line fill suffer endless pain !!!
            actualURL = getClass().getClassLoader().getResource(path);
            if (actualURL == null) {
                throw new FileNotFoundException(url.toExternalForm());
            }
        }
        return actualURL.openConnection();
    }
 }
